package com.xbongbong.saas.analytical.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.ConfigConstant;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.domain.entity.PaasFormExplainEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.PaySheetTypeEnum;
import com.xbongbong.paas.enums.SheetTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.help.FormDataValidateDataHelp;
import com.xbongbong.paas.help.ProSaveHelp;
import com.xbongbong.paas.help.SerialNoHelp;
import com.xbongbong.paas.help.team.TeamUserHelp;
import com.xbongbong.paas.model.PaasFormExplainModel;
import com.xbongbong.paas.pojo.AddMuchFieldPojo;
import com.xbongbong.paas.pojo.dto.AddBatchDTO;
import com.xbongbong.paas.pojo.dto.FormDataAddDTO;
import com.xbongbong.paas.pojo.dto.ValidateDataDTO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.pro.enums.errorcodes.ContractErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.PaymentErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.formdata.pojo.SaasNeedRedundantAttrPojo;
import com.xbongbong.pro.sheet.pojo.SheetAttrPojo;
import com.xbongbong.pro.sheet.pojo.dto.SheetAmountDetailGetDTO;
import com.xbongbong.pro.sheet.pojo.vo.SheetAmountDetailGetVO;
import com.xbongbong.pro.team.TeamAfterVerifyDTO;
import com.xbongbong.pro.user.pojo.ReturnUserAndDepartmentPojo;
import com.xbongbong.saas.analytical.SaasValidateAnalyticalService;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ErrorMessageConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.PayBalanceEntity;
import com.xbongbong.saas.domain.entity.ext.PayPlanSheetEntityExt;
import com.xbongbong.saas.enums.LinkAddOperationFlagEnum;
import com.xbongbong.saas.enums.SheetAloneEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.PaySheetEnum;
import com.xbongbong.saas.enums.business.PaymentSheetEnum;
import com.xbongbong.saas.enums.business.PurchaseEnum;
import com.xbongbong.saas.help.AnalyticalHelp;
import com.xbongbong.saas.help.FundHelp;
import com.xbongbong.saas.help.SaasDataInitHelp;
import com.xbongbong.saas.help.SaasPayPlanHelper;
import com.xbongbong.saas.help.SaasPaySheetHelper;
import com.xbongbong.saas.help.SaveFormatHelp;
import com.xbongbong.saas.help.workflow.PurchaseHelp;
import com.xbongbong.saas.model.PayBalanceModel;
import com.xbongbong.saas.model.PurchaseModel;
import com.xbongbong.saas.service.toolbox.help.SaasSaveHelper;
import com.xbongbong.saas.toolbox.help.FastJsonHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;

import static com.xbongbong.saas.toolbox.help.FastJsonHelper.getDoubleOrDefaultFromFormData;

/**
 * @author 吴峰
 * @date 2021/07/15 17:15
 */
@Service("payPlanSheetAnalyticalServiceImpl")
public class PayPlanSheetValidateAnalyticalServiceImpl implements SaasValidateAnalyticalService {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(PayPlanSheetValidateAnalyticalServiceImpl.class);
    @Resource
    private PaasFormExplainModel paasFormExplainModel;
    @Resource
    private PurchaseModel purchaseModel;
    @Resource
    private FormDataValidateDataHelp formDataValidateDataHelp;
    @Resource
    private PayBalanceModel payBalanceModel;
    @Resource
    private SaasSaveHelper saasSaveHelper;
    @Resource
    private SerialNoHelp serialNoHelp;
    @Resource
    private SaasPaySheetHelper saasPaySheetHelper;
    @Resource
    private SaasPayPlanHelper saasPayPlanHelper;
    @Resource
    private PurchaseHelp purchaseHelp;
    @Resource
    private FundHelp fundHelp;
    @Resource
    private AnalyticalHelp analyticalHelp;
    @Resource
    private TeamUserHelp teamUserHelp;




    @Override
    public List<Integer> type() {
        return Arrays.asList(XbbRefTypeEnum.PAY_SHEET.getCode(),XbbRefTypeEnum.PREPAY.getCode(),
                XbbRefTypeEnum.WRITE_OFF_PREPAY.getCode(),
                XbbRefTypeEnum.RED_WRITE_OFF_PAY.getCode(),
                XbbRefTypeEnum.RED_WRITE_OFF_PREPAY.getCode(),
                XbbRefTypeEnum.RED_PREPAY.getCode(),
                XbbRefTypeEnum.BAD_PAY.getCode(),
                XbbRefTypeEnum.PREPAY_BALANCE.getCode());
    }

    @Override
    public void analyticalData(ValidateDataDTO validateDataDTO) throws XbbException {
        beforeSave(validateDataDTO);
        teamUserHelp.beforeVerifyUserTeam(validateDataDTO);
    }
    
    public void beforeSave(ValidateDataDTO validateDataDTO) throws XbbException {
        try {
            //代表新增还是编辑，如果是编辑就不走校验逻辑 true代表新增 false代表编辑
            boolean isNew = analyticalHelp.isNew(validateDataDTO.getDataId(), validateDataDTO.getTaskId(), validateDataDTO.getCorpid());
            JSONArray amountDetail = validateDataDTO.getAmountDetail();
            JSONObject data = validateDataDTO.getData();
            //因工作流需求，现在把校验逻辑从save里面迁移到beforeSave里面
            SaveFormatHelp.formatLinkBusiness4Save(data, PaySheetEnum.LINK_SUPPLIER.getAttr(), PaySheetEnum.SUPPLIER_LINK_TEXT.getAttr(), PaySheetEnum.LINK_SUPPLIER.getFieldType());
            SaveFormatHelp.formatLinkBusiness4Save(data, PaySheetEnum.LINK_PURCHASE.getAttr(), PaySheetEnum.PURCHASE_LINK_TEXT.getAttr(), PaySheetEnum.LINK_PURCHASE.getFieldType());
            SaveFormatHelp.formatLinkBusiness4Save(data, PaySheetEnum.PAY_PLAN.getAttr(), PaySheetEnum.PAY_PLAN_LINK_TEXT.getAttr(), PaySheetEnum.PAY_PLAN.getFieldType());
            SaveFormatHelp.formatLinkBusiness4Save(data, PaySheetEnum.ORIGIN_ID.getAttr(), PaySheetEnum.ORIGIN_ID_LINK_TEXT.getAttr(), PaySheetEnum.ORIGIN_ID.getFieldType());
            Integer businessType = validateDataDTO.getBusinessType();
            String type = FastJsonHelper.getStringFromFormData(data, PaySheetEnum.PAY_SHEET_TYPE.getAttr());
            PaySheetTypeEnum paySheetTypeEnum = null;
            if (StringUtil.isNotEmpty(type)){
                paySheetTypeEnum = PaySheetTypeEnum.getByCode(type);
            } else {
                paySheetTypeEnum = PaySheetTypeEnum.getByBusinessType(businessType);
                data.put(PaymentSheetEnum.PAYMENT_SHEET_TYPE.getAttr(), paySheetTypeEnum.getCode());
            }
            //处理付款单明细中子回款单的标签
            if(CollectionsUtil.isNotEmpty(amountDetail)){
                saasSaveHelper.dealLabelDataForSheet(amountDetail,validateDataDTO);
            }
            SheetTypeEnum sheetTypeEnum = paySheetTypeEnum.getSheetTypeEnum();

            //把付款单类型置入data内，以让进入审批的data内有付款单类型的值（注：审批内7种类型付款单都是统一为802，其类型已经在data内，所以置入类型时需要判断data内是否已经存在类型）

            //校验明细金额，退货id处理等
            saasSaveHelper.sheetBeforeSave(validateDataDTO, data, sheetTypeEnum);
            if (!Objects.equals(isNew, false)) {
                validateDataDTO.setBusinessType(paySheetTypeEnum.getBusinessType());
                if (Objects.equals(sheetTypeEnum, SheetTypeEnum.WRITE_OFF_PAY)) {
                    //已核销
                    writeOffBeforeSave(validateDataDTO, paySheetTypeEnum);
                } else if (Objects.equals(sheetTypeEnum, SheetTypeEnum.WRITE_OFF_PREPAY)) {
                    //预付款核销
                    writeOffBeforeSave(validateDataDTO, paySheetTypeEnum);
                } else if (Objects.equals(sheetTypeEnum, SheetTypeEnum.RED_WRITE_OFF_PAY)) {
                    //红冲已核销
                    redBeforeSave(validateDataDTO, paySheetTypeEnum);
                } else if (Objects.equals(sheetTypeEnum, SheetTypeEnum.RED_WRITE_OFF_PREPAY)) {
                    //红冲预付款核销
                    redBeforeSave(validateDataDTO, paySheetTypeEnum);
                }else if (Objects.equals(sheetTypeEnum, SheetTypeEnum.PREPAY)) {
                    //预付款
                    prepayBeforeSave(validateDataDTO, paySheetTypeEnum);
                } else if (Objects.equals(sheetTypeEnum, SheetTypeEnum.RED_PREPAY)) {
                    //红冲预付款余额
                    prepayBeforeSave(validateDataDTO, paySheetTypeEnum);
                } else if (Objects.equals(sheetTypeEnum, SheetTypeEnum.BAD_PAY)) {
                    //坏账
                    badBeforeSave(validateDataDTO, paySheetTypeEnum);
                }
            }
            if (CollectionsUtil.isNotEmpty(amountDetail)) {
                SaasNeedRedundantAttrPojo saasNeedRedundantAttrPojo = validateDataDTO.getSaasNeedRedundantAttrPoJo() == null ? new SaasNeedRedundantAttrPojo() : validateDataDTO.getSaasNeedRedundantAttrPoJo();
                saasNeedRedundantAttrPojo.setAmountDetail(amountDetail);
                validateDataDTO.setSaasNeedRedundantAttrPoJo(saasNeedRedundantAttrPojo);
            }
        } catch (XbbException e) {
            throw e;
        } catch (Exception e) {
            LOGGER.error("payPlanSheetAnalyticalServiceImpl.beforeSave error", e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001);
        }
    }

    /**
     * 坏账类型回款单保存之前校验
     *
     * @param validateDataDTO
     * @param paySheetTypeEnum
     */
    private void badBeforeSave(ValidateDataDTO validateDataDTO, PaySheetTypeEnum paySheetTypeEnum) throws XbbException {
        /*
         坏账：付款金额，核销金额一致
         */
        JSONObject dataList = validateDataDTO.getData();
        String corpid = validateDataDTO.getCorpid();
        String userId = validateDataDTO.getUserId();
        if (Objects.nonNull(validateDataDTO.getProcessCreatorId())) {
            userId = validateDataDTO.getProcessCreatorId();
        }
        JSONArray amountDetails = validateDataDTO.getAmountDetail();
        double allAmount = 0D;
        //付款计划和合同对应的付款金额
        Map<Long, Double> contractIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> paymentIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Set<Long> contractIds = new HashSet<>();
        Set<Long> paymentIds = new HashSet<>();
        SheetTypeEnum sheetTypeEnum = paySheetTypeEnum.getSheetTypeEnum();
        //付款单的实际坏账金额
        Double sheetAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(dataList, PaySheetEnum.AMOUNT.getAttr(), 0D);
        SheetAttrPojo sheetAttrPojo = new SheetAttrPojo(sheetTypeEnum.getXbbRefTypeEnum());
        //处理金额明细
        saasSaveHelper.checkAmountDetail(validateDataDTO, sheetAmount, sheetTypeEnum, sheetAttrPojo, dataList, amountDetails, allAmount, contractIdToAmount, paymentIdToAmount, contractIds, paymentIds);
        Double sumAmount = getSumAmount(validateDataDTO, contractIds, paymentIds, null);
        if (Arith.sub(sheetAmount , sumAmount) > ConfigConstant.amountAccuracy) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.BAD_AMOUNT, StringUtil.formatDouble(sumAmount));
        }
        //判断已付金额是否已经超过付款计划内的未付金额
        saasPayPlanHelper.checkPlan(sheetTypeEnum, dataList, corpid, paymentIdToAmount, paymentIds);
        //判断已付金额是否已经超过采购合同内的未付金额
        purchaseHelp.checkContract(sheetTypeEnum, corpid, contractIdToAmount, contractIds);
        PayPlanSheetEntityExt payPlanSheetEntityExt = new PayPlanSheetEntityExt();
        BeanUtil.copyProperties(validateDataDTO, payPlanSheetEntityExt);
        fundHelp.initSheetEntity4Save(userId, payPlanSheetEntityExt);
        for (Map.Entry<String,Object> entry : SaasDataInitHelp.initPayPlanSheet().entrySet()){
            SaasDataInitHelp.antDataSet(dataList,entry.getKey(),entry.getValue());
        }
    }

    /**
     * 预付款、红冲预付款余额类型回款单保存之前校验
     *
     * @param validateDataDTO
     * @param paySheetTypeEnum
     */
    private void prepayBeforeSave(ValidateDataDTO validateDataDTO, PaySheetTypeEnum paySheetTypeEnum) throws XbbException {
         /*
         预付款只关联了客户，其红冲也是如此，因此除了客户余额无需更新采购合同、付款计划
         预付款：只存付款金额，核销金额为空
         红冲预付款余额：只存付款金额、且为负，核销金额为空（需要把金额置为负数，并校验客户余额是否足够红冲）
         */
        JSONObject dataList = validateDataDTO.getData();
        String corpid = validateDataDTO.getCorpid();
        SheetTypeEnum sheetTypeEnum = paySheetTypeEnum.getSheetTypeEnum();
        if (Objects.equals(sheetTypeEnum, SheetTypeEnum.RED_PREPAY)) {
            Double amount = dataList.getDouble(PaySheetEnum.AMOUNT.getAttr());
            //检查红冲预付款余额的红冲金额是否小于客户余额
            saasPaySheetHelper.checkBalance(dataList, corpid, amount);
        }
        for (Map.Entry<String,Object> entry : SaasDataInitHelp.initPayPlanSheet().entrySet()){
            SaasDataInitHelp.antDataSet(dataList,entry.getKey(),entry.getValue());
        }

    }

    /**
     * 红冲已核销、红冲预付款核销类型回款单保存之前校验
     *
     * @param validateDataDTO
     * @param paySheetTypeEnum
     */
    private void redBeforeSave(ValidateDataDTO validateDataDTO, PaySheetTypeEnum paySheetTypeEnum) throws XbbException {
        /*
         红冲已核销、红冲预付款核销：付款金额，核销金额都为负，且一致
         */
        JSONObject dataList = validateDataDTO.getData();
        String corpid = validateDataDTO.getCorpid();
        String userId = validateDataDTO.getUserId();
        if (Objects.nonNull(validateDataDTO.getProcessCreatorId())) {
            userId = validateDataDTO.getProcessCreatorId();
        }
        JSONArray amountDetails = validateDataDTO.getAmountDetail();
        double allAmount = 0D;
        //付款计划和采购合同对应的付款金额
        Map<Long, Double> contractIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> paymentIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Set<Long> contractIds = new HashSet<>();
        Set<Long> paymentIds = new HashSet<>();
        Double sheetAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(dataList, PaySheetEnum.AMOUNT.getAttr(), 0D);
        SheetTypeEnum sheetTypeEnum = paySheetTypeEnum.getSheetTypeEnum();
        SheetAttrPojo sheetAttrPojo = new SheetAttrPojo(sheetTypeEnum.getXbbRefTypeEnum());
        //处理金额明细
        saasSaveHelper.checkAmountDetail(validateDataDTO, sheetAmount, sheetTypeEnum, sheetAttrPojo, dataList, amountDetails, allAmount, contractIdToAmount, paymentIdToAmount, contractIds, paymentIds);
        Long linkPreId = FastJsonHelper.getLongFromFormData(dataList, PaySheetEnum.ORIGIN_ID.getAttr());
        //红冲要对原始付款单关联的已付/部分已付付款计划红冲，需要用前端传入的付款计划ids，否则需要自己查
        Double sumAmount = getSumAmount(validateDataDTO, contractIds, paymentIds, linkPreId);
        if (Arith.sub(sheetAmount , sumAmount) > ConfigConstant.amountAccuracy) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.DETAIL_BEYOND_AMOUNT_RED, PaySheetEnum.ORIGIN_ID.getAttrName());
        }
        //判断已付金额是否已经超过付款计划内的未付金额
        saasPayPlanHelper.checkPlan(sheetTypeEnum, dataList, corpid, paymentIdToAmount, paymentIds);
        //判断已付金额是否已经超过采购合同内的未付金额
        purchaseHelp.checkContract(sheetTypeEnum, corpid, contractIdToAmount, contractIds);
        PayPlanSheetEntityExt payPlanSheetEntityExt = new PayPlanSheetEntityExt();
        BeanUtil.copyProperties(validateDataDTO, payPlanSheetEntityExt);
        fundHelp.initSheetEntity4Save(userId, payPlanSheetEntityExt);
        for (Map.Entry<String,Object> entry : SaasDataInitHelp.initPayPlanSheet().entrySet()){
            SaasDataInitHelp.antDataSet(dataList,entry.getKey(),entry.getValue());
        }
    }

    /**
     * 已核销、预付款核销类型回款单保存之前校验
     *
     * @param validateDataDTO
     * @param paySheetTypeEnum
     */
    private void writeOffBeforeSave(ValidateDataDTO validateDataDTO, PaySheetTypeEnum paySheetTypeEnum) throws XbbException {
        SheetTypeEnum sheetTypeEnum = paySheetTypeEnum.getSheetTypeEnum();
        JSONObject dataList = validateDataDTO.getData();
        String corpid = validateDataDTO.getCorpid();
        String userId = validateDataDTO.getUserId();
        if (Objects.nonNull(validateDataDTO.getProcessCreatorId())) {
            userId = validateDataDTO.getProcessCreatorId();
        }
        JSONArray amountDetails = validateDataDTO.getAmountDetail();
        double allAmount = 0D;
        //付款计划和采购合同对应的付款金额
        Map<Long, Double> contractIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<Long, Double> paymentIdToAmount = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Set<Long> contractIds = new HashSet<>();
        Set<Long> paymentIds = new HashSet<>();
        //回/付款单的实付回/付款金额
        Double sheetAmount;
        boolean writeOffPrepaymentFlag = SheetTypeEnum.isWriteOffPre(sheetTypeEnum);
        SheetAttrPojo sheetAttrPojo = new SheetAttrPojo(sheetTypeEnum.getXbbRefTypeEnum());
        if (writeOffPrepaymentFlag) {
            //预付/付款核销，是用的核销金额
            sheetAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(dataList, sheetAttrPojo.getAttrWriteOffAmount(), 0D);
            //检查预付/付款核销金额是否小于客户/供应商余额
            saasPaySheetHelper.checkBalance(dataList, corpid, sheetAmount);
        } else {
            sheetAmount = FastJsonHelper.getDoubleOrDefaultFromFormData(dataList, sheetAttrPojo.getAttrAmount(), 0D);
        }
        //校验金额明细
        saasSaveHelper.checkAmountDetail(validateDataDTO, sheetAmount, sheetTypeEnum, sheetAttrPojo, dataList, amountDetails, allAmount, contractIdToAmount, paymentIdToAmount, contractIds, paymentIds);
        Double sumAmount = getSumAmount(validateDataDTO, contractIds, paymentIds, null);
        //允许误差小于等于ConfigConstant.amountAccuracy
        if (Arith.sub(sheetAmount , sumAmount) > ConfigConstant.amountAccuracy) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.BEYOND_AMOUNT_PAY);
        }
        //判断已付金额是否已经超过付款计划内的未付金额
        saasPayPlanHelper.checkPlan(sheetTypeEnum, dataList, corpid, paymentIdToAmount, paymentIds);
        //判断已付金额是否已经超过采购合同内的未付金额
        purchaseHelp.checkContract(sheetTypeEnum, corpid, contractIdToAmount, contractIds);
        PayPlanSheetEntityExt payPlanSheetEntityExt = new PayPlanSheetEntityExt();
        BeanUtil.copyProperties(validateDataDTO, payPlanSheetEntityExt);
        fundHelp.initSheetEntity4Save(userId, payPlanSheetEntityExt);
        for (Map.Entry<String,Object> entry : SaasDataInitHelp.initPayPlanSheet().entrySet()){
            SaasDataInitHelp.antDataSet(dataList,entry.getKey(),entry.getValue());
        }
    }

    /**
     * 得到付款单可付款（可红冲、可坏账）金额
     * 付款、坏账：获取采购合同/付款计划的未付
     * 红冲：获取（红冲付款单关联的采购合同/付款计划的）已付
     * @param validateDataDTO 付款单保存dto
     * @param contractIds 创建的付款单关联的采购合同id集合
     * @param paymentIds 创建的付款单关联的付款id集合
     * @param linkPreId 关联原始付款单id
     * @return java.lang.Double
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private Double getSumAmount(ValidateDataDTO validateDataDTO, Set<Long> contractIds, Set<Long> paymentIds, Long linkPreId) throws XbbException {
        SheetAmountDetailGetDTO sheetAmountDetailGetDTO = new SheetAmountDetailGetDTO();
        BeanUtil.copyProperties(validateDataDTO, sheetAmountDetailGetDTO);
        sheetAmountDetailGetDTO.setDataIdList(new ArrayList<>(contractIds));
        //空的数组也作为筛选条件
        sheetAmountDetailGetDTO.setEmptyFilterFlag(true);
        sheetAmountDetailGetDTO.setPaymentIdList(new ArrayList<>(paymentIds));
        sheetAmountDetailGetDTO.setLinkPreId(linkPreId);
        SheetAmountDetailGetVO sheetAmountDetailGetVO = saasPayPlanHelper.payAmountDetailGet(true, sheetAmountDetailGetDTO);
        return sheetAmountDetailGetVO.getSumAmount();
    }


    public AddBatchDTO beforeSaveBatch(FormDataAddDTO formDataSaveDTO, PaySheetTypeEnum paySheetTypeEnum, Long supplierId, Boolean fromApproval) throws XbbException {
        Map<String, Object> params = BeanUtil.convertBean2Map(formDataSaveDTO, true);
        List<PaasFormExplainEntity> explainList = paasFormExplainModel.list(params);
        if (Objects.isNull(explainList) || explainList.isEmpty()) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_EXPLAIN_NOT_EXIST);
        }
        Map<String, FieldAttrEntity> explainMap = ExplainUtil.getExplainMap(explainList.get(0).getExplains(), null);
        JSONObject data = formDataSaveDTO.getDataList();
        SaveFormatHelp.formatLinkBusiness4Save(data, PaySheetEnum.LINK_SUPPLIER.getAttr(), PaySheetEnum.SUPPLIER_LINK_TEXT.getAttr(), PaySheetEnum.LINK_SUPPLIER.getFieldType());
        SaveFormatHelp.formatLinkBusiness4Save(data, PaySheetEnum.LINK_PURCHASE.getAttr(), PaySheetEnum.PURCHASE_LINK_TEXT.getAttr(), PaySheetEnum.LINK_PURCHASE.getFieldType());
        Double purchaseAmount;
        // 来自于合同新建编辑
        SaasNeedRedundantAttrPojo saasNeedRedundantAttrPoJo = formDataSaveDTO.getSaasNeedRedundantAttrPoJo();
        if (Objects.nonNull(saasNeedRedundantAttrPoJo) && Objects.equals(saasNeedRedundantAttrPoJo.getParentBusinessType(), XbbRefTypeEnum.PURCHASE.getCode())) {
            purchaseAmount = formDataSaveDTO.getSaasNeedRedundantAttrPoJo().getParentAmount();
        } else {
            JSONArray idArray = data.getJSONArray(PaySheetEnum.LINK_PURCHASE.getAttr());
            Long dataId = 0L;
            if ( CollectionsUtil.isNotEmpty(idArray) ) {
                dataId = idArray.getLong(0);
            }
            PaasFormDataEntityExt purchaseEntityExt = purchaseModel.getByKey(dataId,formDataSaveDTO.getCorpid());
            if (Objects.isNull(purchaseEntityExt)) {
                throw new XbbException(ContractErrorCodeEnum.API_ERROR_203001);
            }
            purchaseAmount = purchaseEntityExt.getData().getDouble(PurchaseEnum.TOTAL_MONEY.getAttr());
        }
        Double paySheetAmount = 0D;
        Double advancePayBalance = 0D;
        JSONArray paySheetArray = data.getJSONArray(PurchaseEnum.ADD_PAY_SHEET.getAttr());
        String xbbRefTypeName = XbbRefTypeEnum.PAY_SHEET.getName();
        if ( Objects.equals(PaySheetTypeEnum.WRITE_OFF_PREPAY.getCode(), paySheetTypeEnum.getCode()) ){
            paySheetArray = data.getJSONArray(PurchaseEnum.ADD_WRITE_OFF_PREPAY.getAttr());
            xbbRefTypeName = XbbRefTypeEnum.WRITE_OFF_PREPAY.getName();
        }

        List<AddMuchFieldPojo> addBatchList = new ArrayList<>();
        List<AddMuchFieldPojo> delBatchList = new ArrayList<>();
        List<AddMuchFieldPojo> updateBatchList = new ArrayList<>();
        List<ValidateDataDTO> validateDataDTOList = new ArrayList<>();
        FieldAttrEntity serialNoEntity = explainMap.get(PurchaseEnum.SHEET_NO.getAttr());
        for (Object obj : paySheetArray){
            AddMuchFieldPojo addMuchFieldPojo = new AddMuchFieldPojo();
            ValidateDataDTO validateDataDTO = new ValidateDataDTO();
            JSONObject json = (JSONObject) obj;
            Long dataId = json.getLong(StringConstant.DATA_ID);
            addMuchFieldPojo.setId(dataId);
            String serialNo = json.getString(FieldTypeEnum.SERIALNO.getAlias());
            // 开启严控模式
            if (Objects.nonNull(serialNoEntity) && Objects.equals(serialNoEntity.getStrictController(), BasicConstant.ONE) && StringUtil.isEmpty(serialNo)) {
                serialNo = serialNoHelp.getSerialNo(serialNo, formDataSaveDTO.getCorpid(), formDataSaveDTO.getFormId(),  serialNoEntity);
                addMuchFieldPojo.setSerialNo(serialNo);
                formDataSaveDTO.setSerialNo(serialNo);
                json.getJSONObject("data").put(FieldTypeEnum.SERIALNO.getAlias(), serialNo);
            }
            if ( StringUtil.isNotEmpty(serialNo) && serialNo.length()>BasicConstant.SERIAL_LENGTH_NUMBER) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208059,String.format(PaymentErrorCodeEnum.API_ERROR_208059.getMsg(),xbbRefTypeName,BasicConstant.SERIAL_LENGTH_NUMBER));
            }
            addMuchFieldPojo.setSerialNo(serialNo);
            JSONObject paySheetData = json.getJSONObject("data");
            Integer alone = json.getInteger(BasicConstant.ALONE);
            boolean flag = Objects.isNull(dataId) && (Objects.isNull(alone) || Objects.equals(alone, SheetAloneEnum.NORMAL.getCode()));
            if (flag) {
                JSONArray belongIdArray = paySheetData.getJSONArray(PaySheetEnum.BELONG_ID.getAttr());
                if ( belongIdArray.size() > 1 ){
                    throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208047, PaymentErrorCodeEnum.API_ERROR_208047.getMsg(), paySheetTypeEnum.getName());
                }
            }
            for (Map.Entry<String,Object> entry : SaasDataInitHelp.initPayPlanSheet().entrySet()){
                SaasDataInitHelp.antDataSet(paySheetData,entry.getKey(),entry.getValue());
            }
            if ( Objects.equals(PaySheetTypeEnum.WRITE_OFF_PAY.getCode(), paySheetTypeEnum.getCode()) ){
                paySheetData.put(PaySheetEnum.WRITE_OFF_AMOUNT.getAttr(), getDoubleOrDefaultFromFormData(paySheetData, PaySheetEnum.AMOUNT.getAttr(), 0D));
            }
            if ( Objects.isNull(dataId)) {
                paySheetData.put(PaySheetEnum.PAY_SHEET_TYPE.getAttr(), paySheetTypeEnum.getCode());
            }
            paySheetData.put(PaySheetEnum.LINK_SUPPLIER.getAttr(),data.get(PaySheetEnum.LINK_SUPPLIER.getAttr()));
            paySheetData.put(PaySheetEnum.SUPPLIER_LINK_TEXT.getAttr(),data.get(PaySheetEnum.SUPPLIER_LINK_TEXT.getAttr()));
            paySheetData.put(PaySheetEnum.LINK_PURCHASE.getAttr(),data.get(PaySheetEnum.LINK_PURCHASE.getAttr()));
            paySheetData.put(PaySheetEnum.PURCHASE_LINK_TEXT.getAttr(),data.get(PaySheetEnum.PURCHASE_LINK_TEXT.getAttr()));
            Integer operationFlag = json.getInteger(StringConstant.OPERATION_FLAG);
            addMuchFieldPojo.setData(paySheetData);
            if ( Objects.isNull(dataId) ) {
                operationFlag = null;
            }
            BeanUtil.copyProperties(formDataSaveDTO,validateDataDTO);
            validateDataDTO.setSerialNo(serialNo);
            validateDataDTO.setBusinessType(XbbRefTypeEnum.PAY_SHEET.getCode());
            validateDataDTO.setData(addMuchFieldPojo.getData());
            validateDataDTO.setDataId(dataId);
            validateDataDTO.setFromApproval(fromApproval);
            if (Objects.equals(operationFlag, LinkAddOperationFlagEnum.DEL.getCode())){
                delBatchList.add(addMuchFieldPojo);
            }else {
                if (Objects.equals(operationFlag,LinkAddOperationFlagEnum.EDIT.getCode())){
                    updateBatchList.add(addMuchFieldPojo);
                    validateDataDTOList.add(validateDataDTO);
                    validateTeam(paySheetData, validateDataDTO, addMuchFieldPojo);
                }else if (Objects.isNull(dataId)){
                    //为了防止进入审批，前置传入
                    addMuchFieldPojo.setAlone(SheetAloneEnum.NORMAL.getCode());
                    addMuchFieldPojo.setUuid(UUID.randomUUID().toString());
                    addBatchList.add(addMuchFieldPojo);
                    validateDataDTOList.add(validateDataDTO);
                    if ( Objects.equals(PaySheetTypeEnum.WRITE_OFF_PREPAY.getCode(), paySheetTypeEnum.getCode()) ) {
                        advancePayBalance = Arith.add(advancePayBalance, getDoubleOrDefaultFromFormData(paySheetData, PaySheetEnum.WRITE_OFF_AMOUNT.getAttr(), 0D));
                    }
                    validateTeam(paySheetData, validateDataDTO, addMuchFieldPojo);
                }
                if ( Objects.equals(PaySheetTypeEnum.WRITE_OFF_PREPAY.getCode(), paySheetTypeEnum.getCode())  ){
                    paySheetAmount = Arith.add(paySheetAmount, getDoubleOrDefaultFromFormData(paySheetData, PaySheetEnum.WRITE_OFF_AMOUNT.getAttr(), 0D));
                } else {
                    paySheetAmount = Arith.add(paySheetAmount, getDoubleOrDefaultFromFormData(paySheetData, PaySheetEnum.AMOUNT.getAttr(), 0D));
                }
            }
            //校验团队负责人之后需要重新变成原来入参的样子，因为这边付款单校验也会走一次这个ProSaveHelp.analyticalData方法
            JSONArray originalOwnerArray = paySheetData.getJSONArray(FieldTypeEnum.OWNERID.getAlias());
            JSONArray originalCouserArray = paySheetData.getJSONArray(FieldTypeEnum.COUSERID.getAlias());
            validateDataDTO.setExplainMap(explainMap);
            // 标签设置
            SaveFormatHelp.dealLabelData(validateDataDTO.getData(), validateDataDTO.getExplainMap());
            ProSaveHelp.analyticalData(validateDataDTO);
            formDataValidateDataHelp.validateData(validateDataDTO);
            addMuchFieldPojo.setDepartmentId(validateDataDTO.getDepartmentId());
            paySheetData.put(FieldTypeEnum.DEPARTMENTID.getAlias(), validateDataDTO.getDepartmentId());
            paySheetData.put(FieldTypeEnum.OWNERID.getAlias(), originalOwnerArray);
            paySheetData.put(FieldTypeEnum.COUSERID.getAlias(), originalCouserArray);
        }
        if ( Objects.equals(PaySheetTypeEnum.WRITE_OFF_PREPAY.getCode(), paySheetTypeEnum.getCode()) && advancePayBalance > 0 ) {
            PayBalanceEntity payBalanceEntity = payBalanceModel.getBySupplierId(supplierId, formDataSaveDTO.getCorpid());
            if ( payBalanceEntity == null || payBalanceEntity.getAdvancePayBalance() < advancePayBalance ) {
                throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208057, PaymentErrorCodeEnum.API_ERROR_208057.getMsg());
            }
        }
        if (paySheetAmount > purchaseAmount){
            throw new XbbException(PaymentErrorCodeEnum.API_ERROR_208046, PaymentErrorCodeEnum.API_ERROR_208046.getMsg(), paySheetTypeEnum.getName());
        }
        AddBatchDTO addBatchDTO = new AddBatchDTO();
        BeanUtil.copyProperties(formDataSaveDTO,addBatchDTO);
        addBatchDTO.setAddBatchList(addBatchList);
        FieldAttrEntity serialField = explainMap.getOrDefault(PaySheetEnum.PAY_PLAN_NO.getAttr(), new FieldAttrEntity());
        if (Objects.equals(serialField.getNoRepeat(), 1)) {
            Set<String> addSerialNoSet = new HashSet<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            addBatchList.forEach(addBatch-> addSerialNoSet.add(addBatch.getSerialNo()));
            if (!Objects.equals(addSerialNoSet.size(), addBatchList.size())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.NO_REPEAT, serialField.getAttrName());
            }
        }
        Integer total = addBatchList.size() + updateBatchList.size() - delBatchList.size();
        if ( Objects.equals(paySheetTypeEnum.getCode(), PaySheetTypeEnum.WRITE_OFF_PREPAY.getCode()) ){
            if ( total > StringConstant.MAX_WRITE_OFF_PREPAY_COUNT ) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.OVER_STEP_MAX_WRITE_OFF_PREPAY_COUNT, StringConstant.MAX_WRITE_OFF_PREPAY_COUNT);
            }
        } else if (Objects.equals(paySheetTypeEnum.getCode(), PaySheetTypeEnum.WRITE_OFF_PAY.getCode())){
            if ( total > StringConstant.MAX_PAY_SHEET_COUNT ) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100028, ErrorMessageConstant.OVER_STEP_MAX_PAY_SHEET_COUNT, StringConstant.MAX_PAY_SHEET_COUNT);
            }
        }
        addBatchDTO.setAmount(paySheetAmount);
        addBatchDTO.setUpdateBatchList(updateBatchList);
        addBatchDTO.setDelBatchList(delBatchList);
        addBatchDTO.setValidateDataDTOList(validateDataDTOList);
        return addBatchDTO;
    }

    /**
     * 校验团队并且往参数中封装团队信息
     *
     * @param json
     * @param validateDataDTO
     * @param addMuchFieldPojo
     */
    private void validateTeam(JSONObject json, ValidateDataDTO validateDataDTO, AddMuchFieldPojo addMuchFieldPojo) throws XbbException {
        JSONArray teamMainPojoArray = json.getJSONArray(FieldTypeEnum.OWNERID.getAlias());
        JSONArray teamCoPojoArray = json.getJSONArray(FieldTypeEnum.COUSERID.getAlias());
        validateDataDTO.setOwnerIds(null);
        validateDataDTO.setCoUserId(null);
        if (Objects.nonNull(teamMainPojoArray) && teamMainPojoArray.size() > BasicConstant.ZERO) {
            List<ReturnUserAndDepartmentPojo> ownerIds = teamMainPojoArray.toJavaList(ReturnUserAndDepartmentPojo.class);
            validateDataDTO.setOwnerIds(ownerIds);
        }
        if (Objects.nonNull(teamCoPojoArray) && teamCoPojoArray.size() > BasicConstant.ZERO) {
            List<ReturnUserAndDepartmentPojo> coUserIds = teamCoPojoArray.toJavaList(ReturnUserAndDepartmentPojo.class);
            validateDataDTO.setCoUserId(coUserIds);
        }
        try{
            teamUserHelp.beforeVerifyUserTeam(validateDataDTO);
            TeamAfterVerifyDTO teamAfterVerifyDTO = validateDataDTO.getSaasNeedRedundantAttrPoJo().getTeamAfterVerifyDTO();
            addMuchFieldPojo.setTeamAfterVerifyDTO(teamAfterVerifyDTO);
        }catch (XbbException e){
            throw new XbbException(e.getCode(), teamUserHelp.formatErrorMessage(e.getCode(), e.getMsg(), XbbRefTypeEnum.PAYMENT_SHEET));
        }
    }

    /**
     * 保存子付款单时，跳过检验条件
     *
     * @param validateDataDTO
     * @throws XbbException
     */
    public void beforeSaveSkipValidate(ValidateDataDTO validateDataDTO) throws XbbException {
        JSONObject data = validateDataDTO.getData();
        SaveFormatHelp.formatLinkBusiness4Save(data, PaySheetEnum.LINK_SUPPLIER.getAttr(), PaySheetEnum.SUPPLIER_LINK_TEXT.getAttr(), PaySheetEnum.LINK_SUPPLIER.getFieldType());
        SaveFormatHelp.formatLinkBusiness4Save(data, PaySheetEnum.LINK_PURCHASE.getAttr(), PaySheetEnum.PURCHASE_LINK_TEXT.getAttr(), PaySheetEnum.LINK_PURCHASE.getFieldType());
        SaveFormatHelp.formatLinkBusiness4Save(data, PaySheetEnum.PAY_PLAN.getAttr(), PaySheetEnum.PAY_PLAN_LINK_TEXT.getAttr(), PaySheetEnum.PAY_PLAN.getFieldType());
        SaveFormatHelp.formatLinkBusiness4Save(data, PaySheetEnum.ORIGIN_ID.getAttr(), PaySheetEnum.ORIGIN_ID_LINK_TEXT.getAttr(), PaySheetEnum.ORIGIN_ID.getFieldType());

        //把付款单类型置入data内，以让进入审批的data内有付款单类型的值（注：审批内7种类型付款单都是统一为802，其类型已经在data内，所以置入类型时需要判断data内是否已经存在类型）
        PaySheetTypeEnum paySheetTypeEnum = PaySheetTypeEnum.getByBusinessType(validateDataDTO.getBusinessType());
        String type = FastJsonHelper.getStringFromFormData(data, PaySheetEnum.PAY_SHEET_TYPE.getAttr());
        if (StringUtil.isEmpty(type)){
            data.put(PaySheetEnum.PAY_SHEET_TYPE.getAttr(), paySheetTypeEnum.getCode());
        } else {
            paySheetTypeEnum = PaySheetTypeEnum.getByCode(type);
        }
        SheetTypeEnum sheetTypeEnum = paySheetTypeEnum.getSheetTypeEnum();
        //校验明细金额，退货id处理等
        saasSaveHelper.sheetBeforeSave(validateDataDTO, data, sheetTypeEnum);
    }
}
